package org.asiainfo.system.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import org.asiainfo.common.core.constant.CacheNames;
import org.asiainfo.common.core.constant.Constants;
import org.asiainfo.common.core.constant.TenantConstants;
import org.asiainfo.common.core.exception.ServiceException;
import org.asiainfo.common.core.utils.SpringUtils;
import org.asiainfo.common.core.utils.StringUtils;
import org.asiainfo.common.mybatis.stream.core.page.TableDataInfo;
import org.asiainfo.common.mybatis.stream.core.page.PageQuery;
import org.asiainfo.common.tenant.utils.TenantHelper;
import org.asiainfo.system.domain.*;
import org.asiainfo.system.domain.vo.SysTenantVo;
import org.asiainfo.system.domain.bo.SysTenantBo;
import org.asiainfo.system.mapper.*;
import org.asiainfo.system.service.SysTenantService;
import org.asiainfo.system.service.SysUserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.asiainfo.common.core.utils.MapstructUtils;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.mybatisflex.core.paginate.Page;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 租户表(SysTenant)表服务实现类
 *
 * @author dotor-ww
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant> implements SysTenantService {

    private final SysUserMapper sysUserMapper;

    private final SysTenantPackageMapper sysTenantPackageMapper;

    private final SysRoleMapper sysRoleMapper;

    private final SysRoleMenuMapper sysRoleMenuMapper;

    private final SysDeptMapper sysDeptMapper;

    private final SysUserRoleMapper sysUserRoleMapper;

    private final SysDictTypeMapper sysDictTypeMapper;

    private final SysDictDataMapper sysDictDataMapper;

    private final SysConfigMapper sysConfigMapper;

    /**
     * 构建查询条件
     *
     * @param sysTenantBo 租户信息
     * @return 构建结果
     */
    private QueryWrapper buildQueryWrapper(SysTenantBo sysTenantBo) {
        QueryWrapper query = query();
        query.eq(SysTenant::getTenantId, sysTenantBo.getTenantId(), ObjectUtil.isNotNull(sysTenantBo.getTenantId()));
        query.like(SysTenant::getContactUserName, sysTenantBo.getContactUserName(), StringUtils.isNotBlank(sysTenantBo.getContactUserName()));
        query.eq(SysTenant::getContactPhone, sysTenantBo.getContactPhone(), StringUtils.isNotBlank(sysTenantBo.getContactPhone()));
        query.like(SysTenant::getCompanyName, sysTenantBo.getCompanyName(), StringUtils.isNotBlank(sysTenantBo.getCompanyName()));
        query.eq(SysTenant::getLicenseNumber, sysTenantBo.getLicenseNumber(), StringUtils.isNotBlank(sysTenantBo.getLicenseNumber()));
        query.eq(SysTenant::getAddress, sysTenantBo.getAddress(), StringUtils.isNotBlank(sysTenantBo.getAddress()));
        query.eq(SysTenant::getIntro, sysTenantBo.getIntro(), StringUtils.isNotBlank(sysTenantBo.getIntro()));
        query.like(SysTenant::getTenantDomain, sysTenantBo.getTenantDomain(), StringUtils.isNotBlank(sysTenantBo.getTenantDomain()));
        query.eq(SysTenant::getPackageId, sysTenantBo.getPackageId(), ObjectUtil.isNotNull(sysTenantBo.getPackageId()));
        query.eq(SysTenant::getExpireTime, sysTenantBo.getExpireTime(), ObjectUtil.isNotNull(sysTenantBo.getExpireTime()));
        query.eq(SysTenant::getAccountCount, sysTenantBo.getAccountCount(), ObjectUtil.isNotNull(sysTenantBo.getAccountCount()));
        query.eq(SysTenant::getStatus, sysTenantBo.getStatus(), StringUtils.isNotBlank(sysTenantBo.getStatus()));
        query.orderBy(SysTenant::getId);
        return query;
    }

    /**
     * 查询
     *
     * @param sysTenantBo 筛选条件
     * @return 查询结果
     */
    @Override
    public List<SysTenantVo> queryList(SysTenantBo sysTenantBo) {
        QueryWrapper query = buildQueryWrapper(sysTenantBo);
        return listAs(query, SysTenantVo.class);
    }

    /**
     * 通过id查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public SysTenantVo queryById(Long id) {
        return getOneAs(query().eq(SysTenant::getId, id), SysTenantVo.class);
    }

    /**
     * 分页查询
     *
     * @param sysTenantBo 筛选条件
     * @param pageQuery   分页对象
     * @return 查询结果
     */
    @Override
    public TableDataInfo<SysTenantVo> queryPageList(SysTenantBo sysTenantBo, PageQuery pageQuery) {
        QueryWrapper query = buildQueryWrapper(sysTenantBo);
        Page<SysTenantVo> page = pageAs(pageQuery.buildPage(), query, SysTenantVo.class);
        return TableDataInfo.build(page);
    }

    /**
     * 新增数据
     *
     * @param sysTenantBo 实例对象
     * @return 实例对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertByBo(SysTenantBo sysTenantBo) {
        return TenantHelper.ignore(()-> {
            SysTenant add = MapstructUtils.convert(sysTenantBo, SysTenant.class);

            // 生成租户ID
            Long tenantId = IdUtil.getSnowflakeNextId();
            Objects.requireNonNull(add).setTenantId(tenantId);
            boolean flag = save(add);
            if (!flag) {
                throw new ServiceException("创建租户失败!");
            }
            sysTenantBo.setId(add.getId());

            //根据套餐创建角色
            Long roleId = createTenantRole(tenantId, sysTenantBo.getPackageId());

            //创建部门名称
            SysDept sysDept = new SysDept();
            sysDept.setTenantId(tenantId);
            sysDept.setDeptName(sysTenantBo.getCompanyName());
            sysDept.setParentId(Constants.TOP_PARENT_ID);
            sysDept.setAncestors(Constants.TOP_PARENT_ID.toString());
            sysDeptMapper.insertSelective(sysDept);

            Long deptId = sysDept.getDeptId();

            //创建系统用户
            SysUser sysUser = new SysUser();
            sysUser.setTenantId(tenantId);
            sysUser.setDeptId(deptId);
            sysUser.setNickName(sysTenantBo.getUsername());
            sysUser.setUserName(sysTenantBo.getUsername());
            sysUser.setPassword(BCrypt.hashpw(sysTenantBo.getPassword()));
            sysUserMapper.insertSelective(sysUser);

            //设置部门负责人
            SysDept update = new SysDept();
            update.setLeader(String.valueOf(sysUser.getUserId()));
            update.setDeptId(deptId);
            sysDeptMapper.update(sysDept);

            //用户角色关联
            SysUserRole sysUserRole = new SysUserRole(sysUser.getUserId(), roleId);
            sysUserRoleMapper.insert(sysUserRole);

            Long defaultTenantId = TenantConstants.DEFAULT_TENANT_ID;
            List<SysDictType> sysDictTypes = sysDictTypeMapper.selectListByQuery(query().eq(SysDictType::getTenantId, defaultTenantId));
            List<SysDictData> sysDictDataList = sysDictDataMapper.selectListByQuery(query().eq(SysDictData::getTenantId, defaultTenantId));
            for (SysDictType sysDictType : sysDictTypes) {
                sysDictType.setDictId(null);
                sysDictType.setTenantId(tenantId);
            }

            for (SysDictData sysDictData : sysDictDataList) {
                sysDictData.setDictCode(null);
                sysDictData.setTenantId(tenantId);
            }

            sysDictTypeMapper.insertBatch(sysDictTypes);
            sysDictDataMapper.insertBatch(sysDictDataList);

            List<SysConfig> sysConfigs = sysConfigMapper.selectListByQuery(query().eq(SysConfig::getTenantId, defaultTenantId));
            for (SysConfig sysConfig : sysConfigs) {
                sysConfig.setConfigId(null);
                sysConfig.setTenantId(tenantId);
            }
            sysConfigMapper.insertBatch(sysConfigs);
            return Boolean.TRUE;
        });
    }

    /**
     * 修改数据
     *
     * @param sysTenantBo 实例对象
     * @return 实例对象
     */
    @Override
    public Boolean updateByBo(SysTenantBo sysTenantBo) {
        SysTenant sysTenant = MapstructUtils.convert(sysTenantBo, SysTenant.class);
        if (ObjectUtil.isNotNull(sysTenant)) {
            sysTenant.setTenantId(null);
            sysTenant.setPackageId(null);
            return updateById(sysTenant);
        }
        throw new ServiceException("空指针异常!");
    }

    /**
     * 通过主键删除数据
     *
     * @param ids 主键
     * @return 是否成功
     */
    @CacheEvict(cacheNames = CacheNames.SYS_TENANT, allEntries = true)
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        if (ids.contains(TenantConstants.SUPER_ADMIN_ID)) {
            throw new ServiceException("超级租户数据不能删除");
        }
        return removeByIds(ids);
    }

    /**
     * 基于租户ID查询租户信息
     *
     * @param tenantId 租户ID
     * @return 租户信息
     */
    @Cacheable(cacheNames = CacheNames.SYS_TENANT, key = "#tenantId")
    @Override
    public SysTenantVo queryByTenantId(Long tenantId) {
        return getOneAs(query().eq(SysTenant::getTenantId, tenantId), SysTenantVo.class);
    }

    /**
     * 校验租户是否允许操作
     *
     * @param tenantId 租户ID
     */
    @Override
    public void checkTenantAllowed(Long tenantId) {
        if (ObjectUtil.isNotNull(tenantId) && TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
            throw new ServiceException("默认租户不支持操作");
        }
    }

    /**
     * 校验租户账号余额
     *
     * @param tenantId 租户ID
     * @return 校验结果
     */
    @Override
    public Boolean checkAccountBalance(Long tenantId) {
        SysTenantVo sysTenantVo = SpringUtils.getAopProxy(this).queryByTenantId(tenantId);
        if (sysTenantVo.getAccountCount() == -1) return true;
        Long userNum = sysUserMapper.selectCountByQuery(query());
        return sysTenantVo.getAccountCount() - userNum > 0;
    }

    /**
     * 校验有效期
     *
     * @param tenantId 租户ID
     * @return 校验结果
     */
    @Override
    public Boolean checkExpireTime(Long tenantId) {
        SysTenantVo sysTenantVo = SpringUtils.getAopProxy(this).queryByTenantId(tenantId);
        if (ObjectUtil.isNull(sysTenantVo.getExpireTime())) {
            return true;
        }
        //当前时间在过期时间之前通过
        return new Date().before(sysTenantVo.getExpireTime());
    }

    /**
     * 校验公司名称是否唯一
     *
     * @param sysTenantBo 租户信息
     * @return 校验结果
     */
    @Override
    public Boolean checkCompanyNameUnique(SysTenantBo sysTenantBo) {
        return exists(query().eq(SysTenant::getCompanyName, sysTenantBo.getCompanyName()).ne(SysTenant::getTenantId, sysTenantBo.getTenantId(), ObjectUtil.isNotNull(sysTenantBo.getTenantId())));
    }

    /**
     * 根据租户菜单创建角色
     *
     * @param tenantId  租户ID
     * @param packageId 套餐ID
     * @return 角色ID
     */
    private Long createTenantRole(Long tenantId, Long packageId) {
        SysTenantPackage sysTenantPackage = sysTenantPackageMapper.selectOneById(packageId);
        if (ObjectUtil.isNull(sysTenantPackage)) {
            throw new ServiceException("套餐不存在!");
        }
        List<Long> menuIds = StringUtils.splitTo(sysTenantPackage.getMenuIds(), Convert::toLong);
        SysRole sysRole = new SysRole();
        sysRole.setTenantId(tenantId);
        sysRole.setRoleName(TenantConstants.TENANT_ADMIN_ROLE_NAME);
        sysRole.setRoleKey(TenantConstants.TENANT_ADMIN_ROLE_KEY);
        sysRole.setRoleSort(1);
        sysRole.setRoleStatus(TenantConstants.NORMAL);
        sysRoleMapper.insertSelective(sysRole);

        Long roleId = sysRole.getRoleId();
        List<SysRoleMenu> roleMenus = menuIds.stream().map(menuId -> new SysRoleMenu(roleId, menuId)).toList();
        sysRoleMenuMapper.insertBatch(roleMenus);
        return roleId;
    }

    /**
     * 修改租户状态
     *
     * @param sysTenantBo 租户信息
     * @return 修改结果
     */
    @Override
    public Boolean updateTenantStatus(SysTenantBo sysTenantBo) {
        SysTenant sysTenant = MapstructUtils.convert(sysTenantBo, SysTenant.class);
        return updateById(sysTenant);
    }

    /**
     * 同步套餐
     *
     * @param tenantId  租户ID
     * @param packageId 套餐ID
     * @return 同步结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean syncTenantPackage(Long tenantId, Long packageId) {
        //查询当前套餐
        SysTenantPackage sysTenantPackage = sysTenantPackageMapper.selectOneById(packageId);
        //查询当前租户下的所有角色
        List<SysRole> sysRoles = sysRoleMapper.selectListByQuery(query().eq(SysRole::getTenantId, tenantId));
        List<Long> roleIds = new ArrayList<>(sysRoles.size() - 1);
        List<Long> menuIds = StringUtils.splitTo(sysTenantPackage.getMenuIds(), Convert::toLong);
        for (SysRole sysRole : sysRoles) {
            if (TenantConstants.TENANT_ADMIN_ROLE_KEY.equals(sysRole.getRoleKey())) {
                List<SysRoleMenu> roleMenus = new ArrayList<>(menuIds.size());
                menuIds.forEach(menuId -> {
                    SysRoleMenu roleMenu = new SysRoleMenu(sysRole.getRoleId(), menuId);
                    roleMenus.add(roleMenu);
                });
                //删除之前的角色关联菜单信息
                sysRoleMenuMapper.deleteByQuery(query().eq(SysRoleMenu::getRoleId, sysRole.getRoleId()));
                //插入新的角色关联菜单
                sysRoleMenuMapper.insertBatch(roleMenus);
            } else {
                roleIds.add(sysRole.getRoleId());
            }
        }

        if (!roleIds.isEmpty()) {
            sysRoleMenuMapper.deleteByQuery(query()
                .in(SysRoleMenu::getRoleId, roleIds)
                .notIn(SysRoleMenu::getMenuId, menuIds, !menuIds.isEmpty()));
        }

        return Boolean.TRUE;
    }

    /**
     * 动态切换租户
     *
     * @param tenantId 租户ID
     */
    @Override
    public void dynamicTenant(Long tenantId) {
        SpringUtils.getBean(SysUserService.class).updateUseLastTenantIdByUserId(tenantId);
    }
}
